<meta charset="utf-8">
(#) Broken Iterator

!!! WARNING: Broken Iterator
   This is a warning.

Id
:   `BrokenIterator`
Summary
:   Broken Iterator
Severity
:   Warning
Category
:   Correctness
Platform
:   Android
Vendor
:   Android Open Source Project
Feedback
:   https://issuetracker.google.com/issues/new?component=192708
Since
:   3.6.0 (February 2020)
Affects
:   Kotlin and Java files
Editing
:   This check runs on the fly in the IDE editor
See
:   https://developer.android.com/reference/java/util/LinkedHashMap
Implementation
:   [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/IteratorDetector.kt)
Tests
:   [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IteratorDetectorTest.kt)

**For LinkedHashMap:**

The spliterators returned by `LinkedHashMap` in Android Nougat (API
levels 24 and 25) use the wrong order (inconsistent with the iterators,
which use the correct order), despite reporting `Spliterator.ORDERED`.
You may use the following code fragments to obtain a correctly ordered
`Spliterator` on API level 24 and 25:

For a Collection view `c = lhm.entrySet()`, `c = lhm.keySet()` or `c =
lhm.values()`, use `java.util.Spliterators.spliterator(c,
c.spliterator().characteristics())` instead of `c.spliterator()`.

Instead of `c.stream()` or `c.parallelStream()`, use
`java.util.stream.StreamSupport.stream(spliterator, false)` to construct
a (nonparallel) Stream from such a `Spliterator`.

**For Vector:**

The `listIterator()` returned for a `Vector` has a broken `add()`
implementation on Android N (API level 24). Consider switching to
`ArrayList` and if necessary adding synchronization.

(##) Example

Here is an example of lint warnings produced by this check:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~text
src/test/pkg/LinkedHashmapTest.java:34:Warning:
LinkedHashMap#spliterator was broken in API 24 and 25. Workaround: Use
java.util.Spliterators.spliterator(c2a,
c2a.spliterator().characteristics()) [BrokenIterator]
    Spliterator&lt;String&gt; keys2a = c2a.spliterator(); // Warn
                                 -----------------
src/test/pkg/LinkedHashmapTest.java:35:Warning:
LinkedHashMap#spliterator was broken in API 24 and 25. Workaround: Use
java.util.Spliterators.spliterator(c2b,
c2b.spliterator().characteristics()) [BrokenIterator]
    Spliterator&lt;String&gt; keys2b = c2b.spliterator(); // Warn
                                 -----------------
src/test/pkg/LinkedHashmapTest.java:36:Warning:
LinkedHashMap#spliterator was broken in API 24 and 25. Workaround: Use
java.util.Spliterators.spliterator(c2c,
c2c.spliterator().characteristics()) [BrokenIterator]
    Spliterator&lt;Entry&lt;String, String&gt;&gt; keys2c = c2c.spliterator(); // Warn
                                                -----------------
src/test/pkg/LinkedHashmapTest.java:39:Warning: LinkedHashMap#stream was
broken in API 24 and 25. Workaround: Use
java.util.stream.StreamSupport.stream(spliterator, false)
[BrokenIterator]
    Stream&lt;String&gt; stream1 = c2a.stream(); // Warn
                             ------------
src/test/pkg/LinkedHashmapTest.java:40:Warning:
LinkedHashMap#spliterator was broken in API 24 and 25. Workaround: Use
java.util.Spliterators.spliterator(c2a,
c2a.spliterator().characteristics()) [BrokenIterator]
    StreamSupport.stream(c2a.spliterator(), false); // Warn
                         -----------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Here are the relevant source files:

`src/test/pkg/LinkedHashmapTest.java`:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~java linenumbers
package test.pkg;

import android.os.Build;
import androidx.annotation.RequiresApi;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class LinkedHashmapTest {
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void test() {
        Map<String, String> map1 = new HashMap<>();
        Set<String> c1a = map1.keySet();
        Collection<String> c1b = map1.values();
        Set<Entry<String, String>> c1c = map1.entrySet();
        Spliterator<String> keys1a = c1a.spliterator();
        Spliterator<String> keys1b = c1b.spliterator();
        Spliterator<Entry<String, String>> keys1c = c1c.spliterator(); // OK (not a LinkedHashMap)
        Spliterator<String> keys1 = Spliterators.spliterator(c1a, c1a.spliterator().characteristics());// OK

        Map<String, String> map2 = new LinkedHashMap<>();
        Set<String> c2a = map2.keySet();
        Collection<String> c2b = map2.values();
        Set<Entry<String, String>> c2c = map2.entrySet();

        Spliterator<String> keys2a = c2a.spliterator(); // Warn
        Spliterator<String> keys2b = c2b.spliterator(); // Warn
        Spliterator<Entry<String, String>> keys2c = c2c.spliterator(); // Warn
        Spliterator<String> keys2 = Spliterators.spliterator(c2a, c2a.spliterator().characteristics());// OK

        Stream<String> stream1 = c2a.stream(); // Warn
        StreamSupport.stream(c2a.spliterator(), false); // Warn

        Spliterators.spliterator(c2a, c2a.spliterator().characteristics()); // OK
        StreamSupport.stream(keys2, false); // OK
    }
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

`src/test/pkg/kt/LinkedHashmapTest.kt`:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin linenumbers
package test.pkg.kt

import android.os.Build
import androidx.annotation.RequiresApi
import java.util.*
import java.util.stream.StreamSupport

class LinkedHashmapTest {
    @RequiresApi(api = Build.VERSION_CODES.N)
    fun test() {
        val map1 = HashMap<String, String>()
        val c1a = map1.keys
        val c1b = map1.values
        val c1c = map1.entries
        val keys1a = c1a.spliterator()
        val keys1b = c1b.spliterator()
        val keys1c = c1c.spliterator() // OK (not a LinkedHashMap)
        val keys1 = Spliterators.spliterator(c1a, c1a.spliterator().characteristics())// OK

        val map2 = LinkedHashMap<String, String>()
        val c2a = map2.keys
        val c2b = map2.values
        val c2c = map2.entries

        val keys2a = c2a.spliterator() // Warn
        val keys2b = c2b.spliterator() // Warn
        val keys2c = c2c.spliterator() // Warn
        val keys2 = Spliterators.spliterator(c2a, c2a.spliterator().characteristics())// OK

        val stream1 = c2a.stream() // Warn
        StreamSupport.stream(c2a.spliterator(), false) // Warn

        Spliterators.spliterator(c2a, c2a.spliterator().characteristics()) // OK
        StreamSupport.stream(keys2, false) // OK
    }
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can also visit the
[source code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IteratorDetectorTest.kt)
for the unit tests for this check to see additional scenarios.

The above example was automatically extracted from the first unit test
found for this lint check, `IteratorDetector.testLinkedHashMap`.
To report a problem with this extracted sample, visit
https://issuetracker.google.com/issues/new?component=192708.

(##) Suppressing

You can suppress false positives using one of the following mechanisms:

* Using a suppression annotation like this on the enclosing
  element:

  ```kt
  // Kotlin
  @Suppress("BrokenIterator")
  fun method() {
     add(...)
  }
  ```

  or

  ```java
  // Java
  @SuppressWarnings("BrokenIterator")
  void method() {
     add(...);
  }
  ```

* Using a suppression comment like this on the line above:

  ```kt
  //noinspection BrokenIterator
  problematicStatement()
  ```

* Using a special `lint.xml` file in the source tree which turns off
  the check in that folder and any sub folder. A simple file might look
  like this:
  ```xml
  &lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;lint&gt;
      &lt;issue id="BrokenIterator" severity="ignore" /&gt;
  &lt;/lint&gt;
  ```
  Instead of `ignore` you can also change the severity here, for
  example from `error` to `warning`. You can find additional
  documentation on how to filter issues by path, regular expression and
  so on
  [here](https://googlesamples.github.io/android-custom-lint-rules/usage/lintxml.md.html).

* In Gradle projects, using the DSL syntax to configure lint. For
  example, you can use something like
  ```gradle
  lintOptions {
      disable 'BrokenIterator'
  }
  ```
  In Android projects this should be nested inside an `android { }`
  block.

* For manual invocations of `lint`, using the `--ignore` flag:
  ```
  $ lint --ignore BrokenIterator ...`
  ```

* Last, but not least, using baselines, as discussed
  [here](https://googlesamples.github.io/android-custom-lint-rules/usage/baselines.md.html).

<!-- Markdeep: --><style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style><script src="markdeep.min.js" charset="utf-8"></script><script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js" charset="utf-8"></script><script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>